package mall.generator;

import mall.core.jdbc.Repository;
import mall.entity.Menu;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.support.rowset.SqlRowSet;

import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.*;

/**
 * @author xf07b1
 * @wechat xf07b1
 * @description
 */
public class CodeGenerator extends Repository {
    /**
     * 表名
     */
    private static final String TABLE_NAME = "book";

    /**
     * 实体类名称
     */
    private static final String ENTITY_NAME = "Book";

    /**
     * 模块名
     */
    private static final String MODULE_NAME = "book";

    /**
     * 业务名
     */
    private static final String BUSINESS_NAME = "图书";


    /**
     * 列集合
     */
    private static List<Column> columnList;


    private static final String BASE_PACKAGE_PATH = System.getProperty("user.dir") + "/src/main/java/mall/";
    private static final String BASE_VIEW_PATH = System.getProperty("user.dir") + "/src/main/webapp/WEB-INF/views/";

    private static final String space4 = "    ";
    private static final String space6 = space4 + "  ";
    private static final String space8 = space4 + space4;
    private static final String space12 = space4 + space8;

    public static void main(String[] args) throws Exception {

        // 第一步，生成数据库column数据。
        // generateColumnData();

        // 第二步，手动修改数据库column数据，配置增改查的字段

        // 第三步，生成代码
        generateCode();

    }

    private static void generateCode() throws Exception {

        columnList = selectColumnList();

        createEntity();
        createRepository();
        createService();
        createController();
        createCreate();
        createEdit();
        createList();

        insertMenu();
    }

    private static List<Column> selectColumnList() {
        return jdbcTemplate.query("select * from `column` where table_name = ? order by `order` ;", new BeanPropertyRowMapper<>(Column.class), TABLE_NAME);
    }

    private static void generateColumnData() throws SQLException {
        columnList = generateColumnList();

        insertColumn();
    }

    private static void insertColumn() {

        for (Column column : columnList) {
            jdbcTemplate.update("insert into `column` (column_name, column_type, java_field, java_type, primary_key, table_name) values (?, ?, ?, ?, ?, ?);",
                    column.getColumnName(), column.getColumnType(), column.getJavaField(), column.getJavaType(), column.isPrimaryKey(), TABLE_NAME);
        }

    }

    private static void insertMenu() {
        Menu menu = new Menu();
        menu.setIcon("bi bi-grid-fill");
        menu.setPath(TABLE_NAME);
        menu.setTitle(BUSINESS_NAME + "管理");

        jdbcTemplate.update("insert into menu (icon, title, path) values (?, ?, ?);", menu.getIcon(), menu.getTitle(), menu.getPath());
    }

    /**
     * 获取行元数据
     */
    static List<Column> generateColumnList() throws SQLException {
        String sql = "show columns from " + TABLE_NAME;
        SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sql);

        List<Column> columnList = new ArrayList<>();

        while (rowSet.next()) {
            Column column = new Column();
            column.setColumnName(rowSet.getString("Field"));
            column.setColumnType(rowSet.getString("Type"));
            column.setJavaField(StringUtils.toLowerCamelCase(column.getColumnName()));
            column.setJavaType(convertDataType(column.getColumnType()));
            column.setPrimaryKey("PRI".equals(rowSet.getString("KEY")));
            column.setIdType(Objects.requireNonNull(rowSet.getString("EXTRA")).contains("auto_increment") ? IdType.AUTO : IdType.INPUT);
            columnList.add(column);
        }
        return columnList;
    }

    /**
     * 生成Entity
     */
    private static void createEntity() throws SQLException, IOException {
        List<Column> columnList = generateColumnList();
        StringBuilder stringBuilder = new StringBuilder()
                .append("package mall.entity;\n")
                .append("import java.util.Date;\n")
                .append("import java.math.BigDecimal;\n")
                .append("import mall.core.Entity;\n")
                .append("\n")
                .append("public class ").append(ENTITY_NAME).append(" extends Entity {\n");


        // 字段
        for (Column column : columnList) {
            stringBuilder.append(space4).append("private ").append(column.getJavaType()).append(" ").append(column.getJavaField()).append(";\n\n");
        }

        // setter getter
        for (Column column : columnList) {

            // getter
            stringBuilder.append(space4).append("public ").append(column.getJavaType()).append(" get").append(StringUtils.toUpperCamelCase(column.getJavaField())).append("() {\n")
                    .append(space8).append("return ").append(column.getJavaField()).append(";\n")
                    .append(space4).append("}\n\n");

            // setter
            stringBuilder.append(space4).append("public void set").append(StringUtils.toUpperCamelCase(column.getJavaField())).append("(").append(column.getJavaType()).append(" ").append(column.getJavaField()).append(") {\n")
                    .append(space8).append("this.").append(column.getJavaField()).append(" = ").append(column.getJavaField()).append(";\n")
                    .append(space4).append("}\n\n");
        }

        stringBuilder.append("}");

        FileUtils.writeStringToFile(stringBuilder.toString(), BASE_PACKAGE_PATH + "/entity/" + ENTITY_NAME + ".java");
        System.out.println(ENTITY_NAME + ".java 生成成功");
    }

    /**
     * 生成Repository
     */
    static void createRepository() throws IOException, URISyntaxException {
        Map<String, Object> map = baseMap();
        String entityName = (String) map.get("entityName");


        // 新增
        StringJoiner insertColumnJoiner = new StringJoiner(", ", "(", ")");
        StringJoiner insertPlaceholderJoiner = new StringJoiner(", ", "(", ")");
        StringJoiner insertParamJoiner = new StringJoiner(", ");

        // 修改
        StringJoiner updateColumnJoiner = new StringJoiner(", ");
        StringJoiner updateParamJoiner = new StringJoiner(", ");

        // 查询
        StringBuilder queryWhere = new StringBuilder();

        for (Column column : columnList) {
            String upperField = StringUtils.toUpperCamelCase(column.getJavaField());


            if ("1".equals(column.getIsInsert())) {
                insertColumnJoiner.add(column.getColumnName());
                insertPlaceholderJoiner.add("?");
                insertParamJoiner.add(entityName + ".get" + upperField + "()");
            }

            if ("1".equals(column.getIsEdit())) {
                updateColumnJoiner.add(column.getColumnName() + " = ?");
                updateParamJoiner.add(entityName + ".get" + upperField + "()");
            }

            if ("1".equals(column.getIsQuery())) {
                queryWhere.append(space8).append("if (").append(entityName).append(".get").append(upperField).append("() != null) {\n")
                        .append(space12).append("sb.append(\" and ").append(column.getColumnName()).append(" = ?\");\n")
                        .append(space12).append("params.add(").append(entityName).append(".get").append(upperField).append("());\n")
                        .append(space8).append("}\n");
            }


        }
        map.put("insertColumns", insertColumnJoiner.toString());
        map.put("insertValues", insertPlaceholderJoiner.toString());
        map.put("insertParams", insertParamJoiner.toString());


        map.put("updateSets", updateColumnJoiner.toString());
        map.put("updateParams", updateParamJoiner.toString());

        map.put("queryWhere", queryWhere.toString());

        String format = StringUtils.format(FileUtils.readStringFromResource("template/repository.template"), map);
        FileUtils.writeStringToFile(format, BASE_PACKAGE_PATH + "/repository/" + ENTITY_NAME + "Repository.java");
        System.out.println(ENTITY_NAME + "Repository.java 生成成功！");
    }

    /**
     * 生成Service
     */
    static void createService() throws IOException, URISyntaxException {
        Map<String, Object> map = baseMap();
        String format = StringUtils.format(FileUtils.readStringFromResource("template/service.template"), map);
        FileUtils.writeStringToFile(format, BASE_PACKAGE_PATH + "/service/" + ENTITY_NAME + "Service.java");
        System.out.println(ENTITY_NAME + "Service.java 生成成功！");
    }

    /**
     * 生成create.jsp
     */
    static void createCreate() throws IOException, URISyntaxException {
        Map<String, Object> map = baseMap();
        StringBuilder stringBuilder = new StringBuilder();

        for (Column column : columnList) {
            if ("1".equals(column.getIsInsert())) {
                stringBuilder.append(space8).append("<div class=\"form-group\">\n")
                        .append(space12).append("<label for=\"").append(column.getJavaField()).append("\" class=\"control-label\">").append(column.getJavaField()).append("</label>\n")
                        .append(space12).append("<input type=\"text\" class=\"form-control\" id=\"").append(column.getJavaField()).append("\" name=\"").append(column.getJavaField()).append("\" required=\"required\"\n")
                        .append(space12).append(space8).append("oninvalid=\"setCustomValidity('请输入").append(column.getJavaField()).append("！');\" oninput=\"setCustomValidity('');\">\n")
                        .append(space8).append("</div>");
            }
        }
        map.put("html", stringBuilder.toString());

        String format = StringUtils.format(FileUtils.readStringFromResource("template/create.template"), map);
        FileUtils.writeStringToFile(format, BASE_VIEW_PATH + "/admin/" + TABLE_NAME + "/create.jsp");
        System.out.println("create.jsp 生成成功！");
    }

    /**
     * 生成edit.jsp
     */
    static void createEdit() throws IOException, URISyntaxException {
        Map<String, Object> map = baseMap();
        String entityName = (String) map.get("entityName");

        StringBuilder stringBuilder = new StringBuilder();

        for (Column column : columnList) {
            if ("1".equals(column.getIsEdit())) {
                stringBuilder.append(space8).append("<div class=\"form-group\">\n")
                        .append(space12).append("<label for=\"").append(column.getJavaField()).append("\">").append(column.getJavaField()).append("</label>\n")
                        .append(space12).append("<input type=\"text\" class=\"form-control\" id=\"").append(column.getJavaField()).append("\" name=\"").append(column.getJavaField()).append("\" value=\"${").append(entityName).append(".").append(column.getJavaField()).append("}\"\n")
                        .append(space12).append(space8).append("placeholder=\"请输入").append(column.getJavaField()).append("！');\" oninput=\"setCustomValidity('');\">\n")
                        .append(space8).append("</div>");
            }
        }
        map.put("html", stringBuilder.toString());



        String format = StringUtils.format(FileUtils.readStringFromResource("template/edit.template"), map);
        FileUtils.writeStringToFile(format, BASE_VIEW_PATH + "/admin/" + TABLE_NAME + "/edit.jsp");
        System.out.println("edit.jsp 生成成功！");
    }

    /**
     * 生成list.jsp
     */
    static void createList() throws IOException, URISyntaxException {
        Map<String, Object> map = baseMap();

        StringBuilder queryHtml = new StringBuilder();
        StringBuilder headHtml = new StringBuilder();
        StringBuilder valueHtml = new StringBuilder();

        for (Column column : columnList) {
            if ("1".equals(column.getIsQuery())) {
                queryHtml.append(space8).append(space12).append("<div class=\"col-md-3\">\n")
                        .append(space12).append(space12).append("<input type=\"text\" class=\"form-control\" id=\"").append(column.getJavaField()).append("\" name=\"").append(column.getJavaField()).append("\" value=\"${").append(column.getJavaField()).append("}\"").append("placeholder=\"请输入").append(column.getJavaField()).append("\">\n")
                        .append(space8).append(space8).append("</div>");
            }
            if ("1".equals(column.getIsList())) {
                headHtml.append(space8).append(space8).append("<td>").append(column.getJavaField()).append("</td>\n");
                valueHtml.append(space8).append(space8).append("<td>${item.").append(column.getJavaField()).append("}</td>\n");
            }
        }

        map.put("queryHtml", queryHtml.toString());
        map.put("headHtml", headHtml.toString());
        map.put("valueHtml", valueHtml.toString());



        String format = StringUtils.format(FileUtils.readStringFromResource("template/list.template"), map);
        FileUtils.writeStringToFile(format, BASE_VIEW_PATH + "/admin/" + TABLE_NAME + "/list.jsp");
        System.out.println("list.jsp 生成成功！");
    }


    /**
     * 生成Controller
     */
    static void createController() throws IOException, URISyntaxException {
        Map<String, Object> map = baseMap();
        String entityName = (String) map.get("entityName");

        StringBuilder stringBuilder = new StringBuilder();
        StringBuilder getParameter = new StringBuilder();

        for (Column column : columnList) {
            if ("1".equals(column.getIsQuery())) {
                stringBuilder.append(space8).append("request.setAttribute(\"").append(column.getJavaField()).append("\", ").append(entityName).append(".get").append(StringUtils.toUpperCamelCase(column.getJavaField())).append("());\n");
            }
            getParameter.append(space8).append("String ").append(column.getJavaField()).append(" = request.getParameter(\"").append(column.getJavaField()).append("\");\n")
                    .append(space8).append("if (").append(column.getJavaField()).append(" != null && !").append(column.getJavaField()).append(".isEmpty()) {\n")
                    .append(space12).append(entityName).append(".set").append(StringUtils.toUpperCamelCase(column.getJavaField())).append("(").append(column.getJavaType()).append(".valueOf(").append(column.getJavaField()).append("));\n")
                    .append(space8).append("}\n");
        }

        map.put("queryAttribute", stringBuilder.toString());
        map.put("getParameter", getParameter.toString());

        String format = StringUtils.format(FileUtils.readStringFromResource("template/controller.template"), map);
        FileUtils.writeStringToFile(format, BASE_PACKAGE_PATH + "/controller/admin/Admin" + ENTITY_NAME + "Controller.java");
        System.out.println(ENTITY_NAME + "Controller.java 生成成功！");
    }


    private static Map<String, Object> baseMap() {
        Map<String, Object> map = new HashMap<>();

        map.put("EntityName", ENTITY_NAME);
        map.put("entityName", StringUtils.toLowerCamelCase(ENTITY_NAME));
        map.put("moduleName", MODULE_NAME);
        map.put("businessName", BUSINESS_NAME);
        map.put("tableName", TABLE_NAME);

        return map;
    }

    private static String convertDataType(String sqlType) {
        // 提取基础类型和长度
        String baseType = sqlType.split("\\(")[0].toLowerCase().trim();

        switch (baseType) {
            case "varchar":
            case "char":
            case "text":
            case "longtext":
            case "mediumtext":
            case "tinytext":
            case "json":

            case "enum":
            case "set":
                return "String";

            case "int":
            case "integer":
            case "year":
                return "Integer";

            case "smallint":
                return "Short";

            case "mediumint":
            case "bigint":
                return "Long";

            case "tinyint":
                // 根据长度判断：tinyint(1) 是 boolean，其他是 Integer
                return sqlType.matches("tinyint\\(1\\)") ? "Boolean" : "Integer";

            case "float":
                return "Float";

            case "double":
                return "Double";

            case "decimal":
            case "numeric":
                return "BigDecimal";

            case "datetime":
            case "timestamp":
                return "java.time.LocalDateTime";

            case "date":
                return "java.time.LocalDate";

            case "time":
                return "java.time.LocalTime";

            case "bit":
                return "bit(1)".equals(sqlType) ? "Boolean" : "byte[]";

            case "blob":
            case "longblob":
            case "mediumblob":
            case "tinyblob":
            case "binary":
            case "varbinary":
                return "byte[]";

            default:
                System.err.println("未知类型: " + sqlType);
                return "Object";
        }
    }
}
